package com.yuuko.yuukoapi.project.system.controller

import cn.dev33.satoken.secure.BCrypt
import cn.dev33.satoken.stp.StpUtil
import com.yuuko.yuukoapi.common.exception.BizException
import com.yuuko.yuukoapi.common.exception.ErrorCode
import com.yuuko.yuukoapi.common.request.PageParam
import com.yuuko.yuukoapi.common.response.AjaxResult
import com.yuuko.yuukoapi.common.response.None
import com.yuuko.yuukoapi.model.system.Users
import com.yuuko.yuukoapi.model.system.by
import com.yuuko.yuukoapi.model.system.dto.UserInfoView
import com.yuuko.yuukoapi.model.system.dto.UserPageSpec
import com.yuuko.yuukoapi.model.system.dto.UserUpdateInput
import com.yuuko.yuukoapi.repository.system.UsersRepository
import org.babyfish.jimmer.Page
import org.babyfish.jimmer.sql.ast.mutation.SaveMode
import org.babyfish.jimmer.sql.kt.fetcher.newFetcher
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/system/user")
class UsersController(
    val usersRepository: UsersRepository
) {

    @GetMapping("/info")
    fun getUserInfo(): AjaxResult<UserInfoView> {
        val userId = StpUtil.getLoginIdAsString()
        val findUser = usersRepository.findNullable(userId, newFetcher(Users::class).by {
            roles { name() }
            allScalarFields()
        }) ?: throw BizException(ErrorCode.USER_NOT_FOUND)
        val result = UserInfoView(findUser, findUser.roles.map { it.name })
        return AjaxResult.ok(result)
    }

    @GetMapping("/list")
    fun getUserPage(
        page: PageParam,
        spec: UserPageSpec
    ): AjaxResult<Page<Users>> {
        val result = usersRepository.findPage(page, spec, newFetcher(Users::class).by {
            allScalarFields()
            password(false)
            roles { name() }
        })
        return AjaxResult.ok(result)
    }

    @PostMapping
    fun addUser(@RequestBody input: UserUpdateInput): AjaxResult<None?> {
        usersRepository.save(
            input.toEntity { password = BCrypt.hashpw(input.password) },
            SaveMode.INSERT_ONLY
        )
        return AjaxResult.ok()
    }

    @PutMapping("/{id}")
    fun updateUser(@RequestBody input: UserUpdateInput, @PathVariable id: String): AjaxResult<None?> {
        usersRepository.save(
            input.toEntity { this.id = id },
            SaveMode.UPDATE_ONLY
        )
        return AjaxResult.ok()
    }

    @DeleteMapping("/{id}")
    fun deleteUser(@PathVariable id: String): AjaxResult<None?> {
        usersRepository.deleteById(id)
        return AjaxResult.ok()
    }

}